from django.test import TestCase
from django.contrib.auth import get_user_model
from django.urls import reverse
from rest_framework.test import APITestCase
from rest_framework import status
from rest_framework_simplejwt.tokens import RefreshToken
from unittest.mock import patch, MagicMock
from django.core import mail
import json

from .models import User
from .serializers import UserRegistrationSerializer, UserSerializer

User = get_user_model()


class UserModelTestCase(TestCase):
    """用户模型测试"""
    
    def test_create_user(self):
        """测试创建普通用户"""
        user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            phone='13888888888',
            password='testpass123'
        )
        
        self.assertEqual(user.username, 'testuser')
        self.assertEqual(user.email, 'test@example.com')
        self.assertEqual(user.phone, '13888888888')
        self.assertTrue(user.check_password('testpass123'))
        self.assertFalse(user.is_staff)
        self.assertFalse(user.is_superuser)
    
    def test_create_superuser(self):
        """测试创建超级用户"""
        user = User.objects.create_superuser(
            username='admin',
            email='admin@example.com',
            password='adminpass123'
        )
        
        self.assertEqual(user.username, 'admin')
        self.assertTrue(user.is_staff)
        self.assertTrue(user.is_superuser)
    
    def test_user_str_representation(self):
        """测试用户字符串表示"""
        user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        
        self.assertEqual(str(user), 'testuser')
    
    def test_email_normalization(self):
        """测试邮箱标准化"""
        user = User.objects.create_user(
            username='testuser',
            email='Test@Example.COM',
            password='testpass123'
        )
        
        self.assertEqual(user.email, 'Test@example.com')


class UserSerializerTestCase(TestCase):
    """用户序列化器测试"""
    
    def test_user_registration_serializer_valid_data(self):
        """测试用户注册序列化器有效数据"""
        data = {
            'username': 'testuser',
            'email': 'test@example.com',
            'phone': '13888888888',
            'password': 'testpass123',
            'password_confirm': 'testpass123'
        }
        
        serializer = UserRegistrationSerializer(data=data)
        self.assertTrue(serializer.is_valid())
    
    def test_user_registration_password_mismatch(self):
        """测试密码不匹配验证"""
        data = {
            'username': 'testuser',
            'email': 'test@example.com',
            'phone': '13888888888',
            'password': 'testpass123',
            'password_confirm': 'differentpass'
        }
        
        serializer = UserRegistrationSerializer(data=data)
        self.assertFalse(serializer.is_valid())
        self.assertIn('password_confirm', serializer.errors)
    
    def test_user_registration_weak_password(self):
        """测试弱密码验证"""
        data = {
            'username': 'testuser',
            'email': 'test@example.com',
            'phone': '13888888888',
            'password': '123',
            'password_confirm': '123'
        }
        
        serializer = UserRegistrationSerializer(data=data)
        self.assertFalse(serializer.is_valid())
    
    def test_user_registration_duplicate_username(self):
        """测试重复用户名验证"""
        # 创建现有用户
        User.objects.create_user(
            username='testuser',
            email='existing@example.com',
            password='testpass123'
        )
        
        data = {
            'username': 'testuser',
            'email': 'new@example.com',
            'phone': '13888888888',
            'password': 'testpass123',
            'password_confirm': 'testpass123'
        }
        
        serializer = UserRegistrationSerializer(data=data)
        self.assertFalse(serializer.is_valid())
        self.assertIn('username', serializer.errors)
    
    def test_user_registration_duplicate_email(self):
        """测试重复邮箱验证"""
        # 创建现有用户
        User.objects.create_user(
            username='existinguser',
            email='test@example.com',
            password='testpass123'
        )
        
        data = {
            'username': 'newuser',
            'email': 'test@example.com',
            'phone': '13888888888',
            'password': 'testpass123',
            'password_confirm': 'testpass123'
        }
        
        serializer = UserRegistrationSerializer(data=data)
        self.assertFalse(serializer.is_valid())
        self.assertIn('email', serializer.errors)


class AuthenticationAPITestCase(APITestCase):
    """认证API测试"""
    
    def setUp(self):
        self.user_data = {
            'username': 'testuser',
            'email': 'test@example.com',
            'phone': '13888888888',
            'password': 'testpass123'
        }
        
        self.user = User.objects.create_user(**self.user_data)
    
    def test_user_registration(self):
        """测试用户注册"""
        registration_data = {
            'username': 'newuser',
            'email': 'new@example.com',
            'phone': '13999999999',
            'password': 'newpass123',
            'password_confirm': 'newpass123'
        }
        
        url = reverse('user-register')
        response = self.client.post(url, registration_data, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertIn('access_token', response.data)
        self.assertIn('user', response.data)
        
        # 验证用户已创建
        user = User.objects.get(username='newuser')
        self.assertEqual(user.email, 'new@example.com')
    
    def test_user_login(self):
        """测试用户登录"""
        url = reverse('token_obtain_pair')
        login_data = {
            'username': 'testuser',
            'password': 'testpass123'
        }
        
        response = self.client.post(url, login_data, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn('access', response.data)
        self.assertIn('refresh', response.data)
    
    def test_invalid_login(self):
        """测试无效登录"""
        url = reverse('token_obtain_pair')
        login_data = {
            'username': 'testuser',
            'password': 'wrongpassword'
        }
        
        response = self.client.post(url, login_data, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
    
    def test_token_refresh(self):
        """测试Token刷新"""
        # 先登录获取token
        refresh = RefreshToken.for_user(self.user)
        
        url = reverse('token_refresh')
        refresh_data = {
            'refresh': str(refresh)
        }
        
        response = self.client.post(url, refresh_data, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn('access', response.data)
    
    def test_get_user_profile(self):
        """测试获取用户信息"""
        # 设置认证
        refresh = RefreshToken.for_user(self.user)
        access_token = str(refresh.access_token)
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {access_token}')
        
        url = reverse('user-me')
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['username'], 'testuser')
        self.assertEqual(response.data['email'], 'test@example.com')
        # 确保密码不在响应中
        self.assertNotIn('password', response.data)
    
    def test_unauthorized_profile_access(self):
        """测试未授权访问用户信息"""
        url = reverse('user-me')
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)


class PasswordResetTestCase(APITestCase):
    """密码重置测试"""
    
    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
    
    def test_password_reset_request(self):
        """测试密码重置请求"""
        url = reverse('user-password-reset')
        data = {'email': 'test@example.com'}
        
        response = self.client.post(url, data, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        # 在测试环境中，邮件会保存在 django.core.mail.outbox
        self.assertEqual(len(mail.outbox), 1)
        self.assertIn('密码重置', mail.outbox[0].subject)
    
    def test_password_reset_invalid_email(self):
        """测试无效邮箱的密码重置"""
        url = reverse('user-password-reset')
        data = {'email': 'nonexistent@example.com'}
        
        response = self.client.post(url, data, format='json')
        
        # 为了安全考虑，即使邮箱不存在也返回成功
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        # 但不应该发送邮件
        self.assertEqual(len(mail.outbox), 0)
    
    @patch('users.views.default_token_generator.check_token')
    def test_password_reset_confirm(self, mock_check_token):
        """测试密码重置确认"""
        mock_check_token.return_value = True
        
        url = reverse('user-password-reset-confirm')
        data = {
            'user_id': self.user.id,
            'token': 'test-token',
            'new_password': 'newpass123',
            'confirm_password': 'newpass123'
        }
        
        response = self.client.post(url, data, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        
        # 验证密码已更改
        self.user.refresh_from_db()
        self.assertTrue(self.user.check_password('newpass123'))
    
    def test_change_password(self):
        """测试修改密码"""
        # 设置认证
        refresh = RefreshToken.for_user(self.user)
        access_token = str(refresh.access_token)
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {access_token}')
        
        url = reverse('user-change-password')
        data = {
            'old_password': 'testpass123',
            'new_password': 'newpass123',
            'confirm_password': 'newpass123'
        }
        
        response = self.client.post(url, data, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        
        # 验证密码已更改
        self.user.refresh_from_db()
        self.assertTrue(self.user.check_password('newpass123'))
    
    def test_change_password_wrong_old_password(self):
        """测试使用错误旧密码修改密码"""
        # 设置认证
        refresh = RefreshToken.for_user(self.user)
        access_token = str(refresh.access_token)
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {access_token}')
        
        url = reverse('user-change-password')
        data = {
            'old_password': 'wrongpass',
            'new_password': 'newpass123',
            'confirm_password': 'newpass123'
        }
        
        response = self.client.post(url, data, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        
        # 验证密码未更改
        self.user.refresh_from_db()
        self.assertTrue(self.user.check_password('testpass123'))


class UserValidationTestCase(TestCase):
    """用户验证测试"""
    
    def test_phone_validation(self):
        """测试手机号验证"""
        # 有效手机号
        valid_phones = ['13888888888', '15999999999', '18000000000']
        for phone in valid_phones:
            serializer = UserRegistrationSerializer(data={
                'username': f'user_{phone}',
                'email': f'{phone}@example.com',
                'phone': phone,
                'password': 'testpass123',
                'password_confirm': 'testpass123'
            })
            self.assertTrue(serializer.is_valid(), f'Phone {phone} should be valid')
        
        # 无效手机号
        invalid_phones = ['123', '1388888888', '138888888888']
        for phone in invalid_phones:
            serializer = UserRegistrationSerializer(data={
                'username': f'user_{phone}',
                'email': f'{phone}@example.com',
                'phone': phone,
                'password': 'testpass123',
                'password_confirm': 'testpass123'
            })
            self.assertFalse(serializer.is_valid(), f'Phone {phone} should be invalid')
    
    def test_email_validation(self):
        """测试邮箱验证"""
        # 有效邮箱
        valid_emails = ['test@example.com', 'user.name@domain.co.uk', 'user+tag@example.org']
        for email in valid_emails:
            serializer = UserRegistrationSerializer(data={
                'username': f'user_{email.split("@")[0]}',
                'email': email,
                'phone': '13888888888',
                'password': 'testpass123',
                'password_confirm': 'testpass123'
            })
            self.assertTrue(serializer.is_valid(), f'Email {email} should be valid')
        
        # 无效邮箱
        invalid_emails = ['invalid', 'test@', '@example.com', 'test.example.com']
        for email in invalid_emails:
            serializer = UserRegistrationSerializer(data={
                'username': f'user_{hash(email)}',
                'email': email,
                'phone': '13888888888',
                'password': 'testpass123',
                'password_confirm': 'testpass123'
            })
            self.assertFalse(serializer.is_valid(), f'Email {email} should be invalid')


class UserProfileUpdateTestCase(APITestCase):
    """用户资料更新测试"""
    
    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            phone='13888888888',
            password='testpass123'
        )
        
        # 设置认证
        refresh = RefreshToken.for_user(self.user)
        access_token = str(refresh.access_token)
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {access_token}')
    
    def test_update_profile(self):
        """测试更新用户资料"""
        url = reverse('user-me')
        update_data = {
            'email': 'updated@example.com',
            'phone': '13999999999'
        }
        
        response = self.client.patch(url, update_data, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        
        # 验证更新成功
        self.user.refresh_from_db()
        self.assertEqual(self.user.email, 'updated@example.com')
        self.assertEqual(self.user.phone, '13999999999')
    
    def test_update_username_not_allowed(self):
        """测试不允许更新用户名"""
        url = reverse('user-me')
        update_data = {
            'username': 'newusername'
        }
        
        response = self.client.patch(url, update_data, format='json')
        
        # 用户名应该不能更新
        self.user.refresh_from_db()
        self.assertEqual(self.user.username, 'testuser')
    
    def test_update_with_duplicate_email(self):
        """测试更新为重复邮箱"""
        # 创建另一个用户
        other_user = User.objects.create_user(
            username='otheruser',
            email='other@example.com',
            password='testpass123'
        )
        
        url = reverse('user-me')
        update_data = {
            'email': 'other@example.com'
        }
        
        response = self.client.patch(url, update_data, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('email', response.data)